﻿using System;
using System.Collections.Generic;

namespace g4
{
    /// <summary>
    /// 网格边界环平滑器：对网格中的边界环（EdgeLoop）进行平滑处理
    /// 核心原理：通过多轮迭代调整环上顶点的位置，使其更接近相邻顶点的平均值，同时保持环的拓扑结构
    /// </summary>
    public class MeshLoopSmooth
    {
        /// <summary>
        /// 目标网格（包含需要平滑的边界环）
        /// </summary>
        public DMesh3 Mesh;

        /// <summary>
        /// 需要平滑的边界环（由一系列顶点按顺序组成的闭合环）
        /// </summary>
        public EdgeLoop Loop;

        /// <summary>
        /// 平滑系数（控制每次迭代的移动幅度，范围0~1，值越大平滑效果越强）
        /// </summary>
        public double Alpha = 0.25f;

        /// <summary>
        /// 平滑迭代次数（次数越多平滑效果越明显，但计算成本越高）
        /// </summary>
        public int Rounds = 10;

        /// <summary>
        /// 自定义投影函数：用于将平滑后的顶点位置重新投影到特定表面（如约束到原始网格或其他曲面）
        /// 输入：平滑后的位置、顶点ID；输出：投影后的位置
        /// </summary>
        public Func<Vector3d, int, Vector3d> ProjectF;

        /// <summary>
        /// 存储每轮迭代后边界环顶点的平滑位置（临时缓冲区）
        /// </summary>
        Vector3d[] SmoothedPostions;


        /// <summary>
        /// 构造函数：初始化边界环平滑器
        /// </summary>
        /// <param name="mesh">目标网格</param>
        /// <param name="loop">需要平滑的边界环</param>
        public MeshLoopSmooth(DMesh3 mesh, EdgeLoop loop)
        {
            Mesh = mesh;
            Loop = loop;

            // 初始化平滑位置缓冲区（与边界环顶点数量一致）
            SmoothedPostions = new Vector3d[Loop.Vertices.Length];

            // 初始投影函数为null（不进行额外投影）
            ProjectF = null;
        }


        /// <summary>
        /// 验证操作的合法性：检查边界环是否为有效的边环
        /// </summary>
        /// <returns>验证状态（Ok表示合法）</returns>
        public virtual ValidationStatus Validate()
        {
            return MeshValidation.IsEdgeLoop(Mesh, Loop);
        }


        /// <summary>
        /// 执行边界环平滑操作
        /// </summary>
        /// <returns>操作是否成功</returns>
        public virtual bool Smooth()
        {
            int vertexCount = Loop.Vertices.Length;
            if (vertexCount == 0)
                return true; // 空环无需平滑

            // 限制平滑系数和迭代次数在有效范围内
            double smoothFactor = MathUtil.Clamp(Alpha, 0, 1);
            int iterations = MathUtil.Clamp(Rounds, 0, 10000);
            if (iterations == 0)
                return true; // 迭代次数为0，无需处理

            // 多轮平滑迭代
            for (int round = 0; round < iterations; ++round)
            {
                // 并行计算每个顶点的平滑位置
                gParallel.ForEach(Interval1i.Range(vertexCount), (i) => {
                    // 获取当前顶点及其前序、后序顶点（环是闭合的，使用取模确保索引有效）
                    int currentIdx = (i + 1) % vertexCount;
                    int prevIdx = i;
                    int nextIdx = (i + 2) % vertexCount;

                    int currentVid = Loop.Vertices[currentIdx];
                    int prevVid = Loop.Vertices[prevIdx];
                    int nextVid = Loop.Vertices[nextIdx];

                    // 获取顶点坐标
                    Vector3d prevPos = Mesh.GetVertex(prevVid);
                    Vector3d currentPos = Mesh.GetVertex(currentVid);
                    Vector3d nextPos = Mesh.GetVertex(nextVid);

                    // 计算前序和后序顶点的中点（平滑目标位置）
                    Vector3d centroid = (prevPos + nextPos) * 0.5;

                    // 平滑公式：当前位置与目标位置的加权平均
                    SmoothedPostions[i] = (1 - smoothFactor) * currentPos + smoothFactor * centroid;
                });

                // 并行将平滑后的位置写回网格（应用可选的投影）
                gParallel.ForEach(Interval1i.Range(vertexCount), (i) => {
                    int currentIdx = (i + 1) % vertexCount;
                    int currentVid = Loop.Vertices[currentIdx];
                    Vector3d smoothedPos = SmoothedPostions[i];

                    // 若有自定义投影函数，应用投影调整
                    if (ProjectF != null)
                        smoothedPos = ProjectF(smoothedPos, currentVid);

                    // 更新顶点位置
                    Mesh.SetVertex(currentVid, smoothedPos);
                });
            }

            return true;
        }
    }
}